package com.cd.university.service.impl;

import com.cd.university.common.RestResult;
import com.cd.university.common.UniversityConstants;
import com.cd.university.common.enums.ResponseEnum;
import com.cd.university.common.obs.ObsMultipartUpload;
import com.cd.university.common.obs.ObsUtils;
import com.cd.university.common.utils.MyIdGenerator;
import com.cd.university.controller.form.UploadFileDto;
import com.cd.university.db.mapper.ProjectFileMapper;
import com.cd.university.db.mapper.ProjectMapper;
import com.cd.university.db.pojo.Project;
import com.cd.university.db.pojo.ProjectFile;
import com.cd.university.db.pojo.ProjectFileKey;
import com.cd.university.service.ProjectFileService;
import com.cd.university.service.ProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.IdGenerator;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author 陈栋
 * @create 2021/10/15 20:33
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class ProjectFileServiceImpl implements ProjectFileService {

    @Autowired
    private ObsUtils obsUtils;

    @Autowired
    private ProjectFileMapper projectFileMapper;

    @Autowired
    private ProjectMapper projectMapper;

    @Autowired
    private ProjectServiceImpl projectService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ObsMultipartUpload obsMultipartUpload;

    /**
     * 大文件信息信息Set集合，如果文件上传成功，那么就不会存在在集合中
     */
    private static HashMap<String, ProjectFile> bigProjectFileMap = new HashMap<>();

    @Override
    public RestResult updateProjectLogo(Integer projectId, MultipartFile logo) {
        if (!checkIsImage(logo)) {
            return RestResult.failure("该文件不是图片，请重新上传");
        }

        Project project = projectMapper.selectByPrimaryKey(projectId);
        if (project==null) {
            return RestResult.failure(ResponseEnum.PROJECT_NOT_EXISTS.getMsg());
        }

        // 拿到文件名
        String originalFilename = logo.getOriginalFilename();
        // 文件夹名称
        String folder = getFolderName(projectId);

        // 向OBS上传项目logo,文件夹路径为projectFile/${projectId}/logo
        obsUtils.uploadFile(logo, originalFilename, folder);

        ProjectFile projectFile = new ProjectFile();
        projectFile.setProjectId(projectId);
        projectFile.setFileId("logo");
        projectFile.setFileName(originalFilename);
        projectFile.setAtId(0);
        projectFile.setCategoryId(0);
        projectFile.setStatus(UniversityConstants.PROJECT_FILE_ANNEX);
        // TODO: 2021/10/19 这里的项目url默认为1年，过期之后如何处理？
        // 把刚刚上传的文件获得url，给projectFIle表中的path字段
        projectFile.setPath(getFileUrl(originalFilename, projectId));

        // 更新redis
        projectService.updateRedis(projectFile);

        projectFileMapper.insert(projectFile);

        return RestResult.success("上传项目logo成功")
                .put("data",projectFile);
    }

    @Override
    public RestResult downloadProjectLogo(HttpServletResponse response, Integer projectId) {
        ValueOperations opsForValue = redisTemplate.opsForValue();
        List<ProjectFile> projectFiles = (List<ProjectFile>) opsForValue.get("projectFiles");

        String logoUrl = null;
        for (ProjectFile projectFile : projectFiles) {
            if (projectFile.getProjectId().equals(projectId) && projectFile.getFileId().equals("logo")) {
                logoUrl =  projectFile.getPath();
            }
        }

        return RestResult.success("获取" +projectId + "项目logo成功")
                .put("data",logoUrl);
    }

    @Override
    public RestResult updateProjectFile(MultipartFile[] files,
                                        Integer projectId,
                                        Integer atId,
                                        Integer categoryId,
                                        Integer status) {
        // 遍历所有的文件
        for (MultipartFile file : files) {
            ProjectFile projectFile = getProjectFile(file, projectId, atId, categoryId, status);

            // 向项目文件表中插入
            int row = projectFileMapper.insert(projectFile);

            if (row<1) {
                return RestResult.failure(ResponseEnum.FILE_UPDATE_FAILURE.getMsg());
            }

            // 更新redis
            projectService.updateRedis(projectFile);
        }

        return RestResult.success("批量上传项目文件成功");
    }

    @Override
    public RestResult searchProjectFile(Integer projectId, String fileId) {
        ProjectFile res = null;
        res = projectFileMapper.selectByPrimaryKey(new ProjectFileKey(projectId,fileId));

        if (res==null) {
            return RestResult.failure("项目文件不存在啊，你再试试呢！");
        }
        return RestResult.success("查询项目文件成功")
                .put("data",res);
    }

    @Override
    public RestResult updateBigFile(String identifier,
                                    Integer projectId,
                                    Integer atId,
                                    Integer categoryId,
                                    Integer status,
                                    String fileName) {
        ProjectFile projectFile = new ProjectFile();
        projectFile.setFileId(MyIdGenerator.getInstance().getRandomNumber());
        projectFile.setProjectId(projectId);
        projectFile.setStatus(status);
        projectFile.setCategoryId(categoryId);
        projectFile.setAtId(atId);
        projectFile.setFileName(fileName);

        // 存入map中
        bigProjectFileMap.put(identifier, projectFile);
        return RestResult.success("文件基本数据上传成功，等待文件上传");
    }

    @Override
    public RestResult updateBigFile(HttpServletRequest request, UploadFileDto uploadFileDto) {
        boolean uploadPart = obsMultipartUpload.uploadPartToLocal(request,uploadFileDto);

        // 当上传成功之后
        if (uploadPart) {
            ProjectFile projectFile = null;
            while ((projectFile = bigProjectFileMap.get(uploadFileDto.getIdentifier()))==null) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            obsMultipartUpload.partFileMerge(uploadFileDto,getFilePath(getFolderName(projectFile.getProjectId()),projectFile.getFileName()));

            // 更新ProjectServiceImpl中的Redis的数据
            projectService.updateRedis(projectFile);
            // 插入数据库
            projectFileMapper.insertSelective(projectFile);

            RestResult.success("此时文件共计"+Math.round((uploadFileDto.getTotalSize()/1024))+"KB,全部上传成功" +
                    "此时上传总共：" + uploadFileDto.getTotalSize() + "个切片");
        }

        return RestResult.success("第：" + uploadFileDto.getChunkNumber() + "切片上传成功");
    }

    /**
     * 更新数据库中的项目文件的状态
     * @param projectId 项目id
     * @param statusId 状态
     * @param fileId 成果形式的主键文件
     * @return 是否更新成功
     */
    public boolean updateDbProjectFileStatus(Integer projectId, Integer statusId, String fileId) {
        ProjectFile projectFile = new ProjectFile();
        projectFile.setProjectId(projectId);
        projectFile.setFileId(fileId);
        projectFile.setStatus(statusId);
        int row = projectFileMapper.updateByPrimaryKeySelective(projectFile);

        return row==1?true:false;
    }

    /**
     * 检查上传的文件是否是图片
     * @param multipartFile 文件
     * @return 是否是图片
     */
    private boolean checkIsImage(MultipartFile multipartFile) {

        // 原始文件名称
        String fileName = multipartFile.getOriginalFilename();

        // 解析到文件后缀，判断是否合法
        int index = fileName.lastIndexOf(".");
        String suffix = null;
        if (index == -1 || (suffix = fileName.substring(index + 1)).isEmpty()) {
            return false;
        }

        // 允许上传的文件后缀列表
        Set<String> allowSuffix = new HashSet<>(Arrays.asList("jpg", "jpeg", "png", "gif"));
        if (!allowSuffix.contains(suffix.toLowerCase())) {
            return false;
        }
        return true;
    }

    /**
     * 获得项目的文件夹名称
     * @param projectId 项目id
     * @return 项目的文件夹名称
     */
    private String getFolderName(Integer projectId) {
        // 文件夹名称
        String folder = UniversityConstants.PROJECT_FOLDER + "/" + projectId;
        return folder;
    }

    /**
     * 拿到项目文件的Url
     * @param fileName 文件名
     * @param projectId 项目id
     * @return 文件的url
     */
    private String getFileUrl(String fileName,Integer projectId) {
        String fileUrl = obsUtils.getFileUrl(fileName, getFolderName(projectId));
        return fileUrl;
    }

    /**
     * 上传项目文件
     * @param file 多文件
     * @param projectId 项目id
     * @param atId 成果形式id，默认为成果类型
     * @param categoryId 具体的成果id，默认为1
     * @param status 项目文件状态，默认为未通过：1
     * @return 项目文件实体类
     */
    public ProjectFile getProjectFile(MultipartFile file,
                                       Integer projectId,
                                       Integer atId,
                                       Integer categoryId,
                                       Integer status) {
        return getProjectFile(file, projectId, null, atId, categoryId, status);
    }


    /**
     * 上传项目文件
     * @param file 多文件
     * @param projectId 项目id
     * @param projectId 文件id
     * @param atId 成果形式id，默认为成果类型
     * @param categoryId 具体的成果id，默认为1
     * @param status 项目文件状态，默认为未通过：1
     * @return 项目文件实体类
     */
    public ProjectFile getProjectFile(MultipartFile file,
                                      Integer projectId,
                                      String fileId,
                                      Integer atId,
                                      Integer categoryId,
                                      Integer status) {
        ProjectFile projectFile = new ProjectFile();

        String fileName = file.getOriginalFilename();

        projectFile.setFileName(fileName);
        projectFile.setProjectId(projectId);
        // FileId首先判断是否为空，如果为空，那么调用自定义的Id生成器
        projectFile.setFileId(fileId==null?MyIdGenerator.getInstance().getRandomNumber():fileId);
        projectFile.setAtId(atId);
        projectFile.setCategoryId(categoryId);
        projectFile.setStatus(status);

        // 把文件上传到OBS中，并且拿到url，放入到文件的path中
        boolean update = obsUtils.uploadFile(file, fileName, getFolderName(projectId));

        // 更新ProjectServiceImpl中的Redis的数据
        projectService.updateRedis(projectFile);

        // 如果上传失败，返回null值
        if (!update) {
            return null;
        }

        // 拿到url
        String fileUrl = obsUtils.getFileUrl(fileName, getFolderName(projectId));
        projectFile.setPath(fileUrl);

        return projectFile;
    }

    /**
     * 将文件夹名称以及文件名称拼接在一起
     * @param folderName 文件夹名称
     * @param objectKey 文件对象名
     * @return 文件夹/文件对象名
     */
    private String getFilePath(String folderName, String objectKey) {
        return folderName + "/" + objectKey;
    }

    @Async
    @Scheduled(fixedRate = 1000 * 60 * 60)
    public void deleteUselessFile() {
        projectFileMapper.deleteUselessFile();
    }
}
